package smallfrench.moteur.biblio;

import java.util.ArrayList;

import smallfrench.moteur.biblio.err.ErreurParametres;
import smallfrench.moteur.biblio.err.ExceptionInterruption;
import smallfrench.moteur.interp.IListeInstructions;
import smallfrench.moteur.interp.Portee;
import smallfrench.moteur.interp.RObjet;
import smallfrench.moteur.interp.err.ExceptionRetourFonction;
import smallfrench.moteur.noeuds.NBlocFonction;
import smallfrench.moteur.noeuds.Noeud;

/**
 * Une fonction écrite par l’utilisateur.
 * @author antoine1023
 *
 */
public class RFonctionScript extends RFonction implements IListeInstructions {

	private final NBlocFonction source;
	private final Portee porteeGlobale;
	private final Noeud[] noeuds;

	public RFonctionScript(NBlocFonction source, Portee porteeGlobale) {

		this.source = source;
		this.porteeGlobale = porteeGlobale;

		ArrayList<Noeud> al = source.noeuds;
		noeuds = al.toArray(new Noeud[al.size()]);
	}

	//-------------------------------------------

	@Override
	public RObjet appeler(RObjet moi, RObjet[] args) {

		/* Pour éviter les boucles infinies, on regarde si le thread a
		 * été interrompu par l’utilisateur. */
		if (Thread.interrupted())
			throw ExceptionInterruption.nouvelleException();

		Portee variablesLocales = new Portee(porteeGlobale, moi);

		ArrayList<String> nomsParametres = source.nomsParametres;

		if (args.length != nomsParametres.size()) {
			new ErreurParametres("Le nombre de paramètres passés à la "
					+ "fonction ne correspond pas au nombre de paramètres de "
					+ "la fonction").lancer();
		}

		int i = 0;
		// Arguments déclarés et passés en paramètres
		for (; i < args.length ; i++) {
			variablesLocales.ajouterVariable(nomsParametres.get(i), args[i]);
		}
		// Arguments déclarés non fournis : on passe null.
		for (; i < nomsParametres.size() ; i++) {
			variablesLocales.ajouterVariable(nomsParametres.get(i), null);
		}

		RObjet o = RRien.RIEN;

		try {
			for (Noeud j : noeuds) {
				o = j.evaluer(variablesLocales);
			}
		} catch(ExceptionRetourFonction t) {
			// On intercepte un éventuel "retour"
			return t.retour;
		}

		// Si aucune instruction retour n’a été exécutée, on retourne
		// le résultat de la dernière expression.
		return o;
	}

	@Override
	public Portee getPortee() {
		return porteeGlobale;
	}

	@Override
	public Noeud[] getNoeuds() {
		return noeuds;
	}
}
